﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Xml.Linq;
using System.Diagnostics;
using System.Xml.Serialization;
using System.Reflection;


namespace devtm
{

    /// <summary>
    /// node specialisé pour les menus
    /// permet d'encapsuler un object qui n' a pas etait pensé pour une hierarchie
    /// </summary>
    /// <typeparam name="T">object encapsulé</typeparam>
    /// <typeparam name="U">type de la clef</typeparam>
    [Serializable()]
    public class Node<T, U>
    {

        public Node() { }
        public Node(T item, U key, U parent, String libelle) 
        {
            Item = item;
            Key = key;
            Parent = parent;
            Libelle = libelle;
        }

        public IEnumerable<Node<T, U>> List { get; set; }
        public Node(IEnumerable<Node<T, U>> list, T item, U key, U parent, String libelle) : this(item,key, parent,libelle)
        {
            List = list;
        }

        public U Key { get; set; }
        public U Parent { get; set; }
        public String Libelle { get; set; }

        [SoapIgnore(),
        System.Xml.Serialization.XmlIgnore(),
        System.Runtime.Serialization.IgnoreDataMember()]
        public T Item { get; set; }

        public int Level
        {
            get
            {
                if (this.Parent != null)
                    foreach (Node<T, U> item in List)
                    {
                        if (Equals(item.Key, this.Parent))
                            return item.Level + 1;
                    }

                return 0;
            }
        }
        

        public override string ToString()
        {
            return base.ToString();
        }

        public IEnumerable<Node<T, U>> Children()
        {

            foreach (Node<T, U> item in List)
                if (Equals(item.Parent, Key))
                    yield return item;

        }

        #region serialization

        /// <summary>
        /// serialise the hierarchy with the mask
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <param name="mask">The mask.</param>
        /// <param name="parameters">The parameters.</param>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public string ToString(Func<ArgsNode<T, U>, String> mask, object parameters)
        {
            Parameters param = new Parameters(parameters);
            return ToString(mask, param);
        }


        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// serialise the hierarchy with the mask
        /// </summary>
        /// <param name="mask">The mask.</param>
        /// <param name="parameters">The parameters.</param>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public string ToString(Func<ArgsNode<T, U>, String> mask, Parameters parameters)
        {

            // children
            StringBuilder s = new StringBuilder();
            foreach (Node<T, U> item in this.Children())
                s.Append(item.ToString(mask, parameters));

            // item
            if (Item != null)
                return (string)mask(new ArgsNode<T, U>() 
                    { 
                        Item = Item,
                        Node = this, 
                        Children = s.ToString(),
                        Parameters = parameters,
                        Level = Level
                    });

            return s.ToString();

        }

        #endregion

    }


    public class Tree<T, U> : XElement
    {

        private Func<T, U> _keyitem;

        #region ctor

        internal Tree(XName name, Object content) : base(name, content) { }

     
        public static Tree<T, U> Create(IEnumerable<T> list, Func<T, U> keyitem, Func<T, U> keyparent, U valuestart)
        {

            Tree<T, U> items = new Tree<T, U>("items", new XAttribute("root", ""));
            T[] lst = list.ToArray();
            int index = -1;
            while (index++ < lst.Count() - 1)
            {
                T child = lst[index];
                U keyParent = (U)keyparent.DynamicInvoke(child);
                if (Equals(keyParent, valuestart))
                {
                    U keyItem = (U)keyitem.DynamicInvoke(child);
                    String k = keyItem == null ? "null" : keyItem.ToString();
                    Tree<T, U> item = new Tree<T, U>("item"
                        , new XAttribute("key", k)) { Item = child, _keyitem = keyitem };

                    items.Add(item);
                    Create(lst, keyitem, keyparent, keyItem, item);
                }

            }

            return items;
        }

        private static void Create(T[] lst, Func<T, U> keyitem, Func<T, U> keyparent, U valueParent, Tree<T, U> root)
        {

            int index = -1;
            while (index++ < lst.Count() - 1)
            {
                T child = lst[index];
                U keyParent = (U)keyparent.DynamicInvoke(child);
                if (Equals(keyParent, valueParent))
                {
                    U keyItem = (U)keyitem.DynamicInvoke(child);
                    String k = keyItem == null ? "null" : keyItem.ToString();
                    Tree<T, U> item = new Tree<T, U>("item", new XAttribute("key", k)) { Item = child, _keyitem = keyitem };
                    root.Add(item);
                    Create(lst, keyitem, keyparent, keyItem, item);
                }
            }

        }

        #endregion

        #region serialization
        
        public string ToString(Func<MyArgs<T, U>, String> mask)
        {
            // children
            StringBuilder s = new StringBuilder();
            foreach (Tree<T, U> item in this.Elements())
                s.Append(item.ToString(mask));

            // item
            if (Item != null)
                return (string)mask(new MyArgs<T, U>() { Item = Item, Children = s.ToString(), Node = this, keyitemFunc = _keyitem });

            return s.ToString();

        }
   
        #endregion

        #region Find
        
        public Tree<T, U> Find(U key)
        {
            foreach (Tree<T, U> item in this.Elements())
            {
                if (Equals(item._keyitem(item), key))
                    return item;
                
                Tree<T, U> ret = Find(item, key);
                if (ret != null)
                    return ret;
            }

            return null;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="root"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        /// <example>String aa = menu.ToString(p => p.Item.buildULMenu(p, true));</example>
        private Tree<T, U> Find(Tree<T, U> root, U key)
        {
            foreach (Tree<T, U> item in root.Elements())
            {
                if (Equals(item._keyitem(item), key))
                    return item;

                Tree<T, U> ret = Find(item, key);
                if (ret != null)
                    return ret;
            }

            return null;
        }

        #endregion

        public void Run(Action<MyArgs<T, U>> mask)
        {
            // children
            foreach (Tree<T, U> item in this.Elements())
                item.Run(mask);

            // item
            if (Item != null)
                mask(new MyArgs<T, U>() { Item = Item, Node = this});

        }

        public T Item { get; internal set; }
        public int Level
        {
            get
            {
                if (this.Ancestors().Count() == 0)
                    return 0;
                else
                    return ((Tree<T, U>)this.Ancestors().FirstOrDefault()).Level + 1;
            }
        }
        public static implicit operator T(Tree<T, U> item) { return item.Item; }


    }


    #region args
    
    public class MyArgs<T, U>
    {

        public Tree<T, U> Node { get; set; }
        public T Item { get; set; }
        public int Level { get; set; }
        public string Children { get; set; }
        public bool HasChildren { get { return !String.IsNullOrEmpty(Children) ;} }
        public Func<T, U> keyitemFunc;

    }

    public class ArgsNode<T, U>
    {

        public Node<T, U> Node { get; set; }
        public T Item { get; set; }
        public int Level { get; set; }
        public bool HasChildren { get { return !String.IsNullOrEmpty(Children); } }
        public string Children { get; set; }
        public Parameters Parameters;
    }

    #endregion

}
